﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Drawing;

using Noris.Tools.TraceVisualiser.Interface;
using Noris.Tools.TraceVisualiser.Support;
using Noris.Tools.TraceVisualiser.Components;
using Noris.Tools.TraceVisualiser.TraceData;

namespace Noris.Tools.TraceVisualiser.Apollo.AplServer
{
    #region TraceReader : reader pro soubory typu Aplikační trace Noris
    /// <summary>
    /// TraceReader : reader pro soubory typu Aplikační trace Noris
    /// </summary>
	public class TraceReader : ITraceReader, IColorDeclarator
    {
		#region IPlugin Members
		/// <summary>
		/// Příznak, že Plugin je aktivní ve standardním běhu (tj. při běžném užiavtelském spuštění).
		/// Plně funkční pluginy tedy vrací true.
		/// Pluginy, které jsou ve vývoji, vrací false, takové jsou zobrazeny / spouštěny pouze v aplikaci, která běží v režimu Debug.
		/// </summary>
		bool IPlugin.Active { get { return true; } }
		#endregion
		#region ITraceReader, IDisposable Members
        /// <summary>
        /// Zde reader vrací výraz, který se použije v dialogu při výběru vstupních trace soubor.
        /// Různé readery mají různé vstupní soubory s různou příponou, a my nechceme žádný reader opomenout.
        /// Reader může umět zpracovávat více různých koncovek, proto se používá string[], každá položka obsahuje jeden filtr.
        /// Každý jednotlivý filtr je ve tvaru: "Textový popisek|filtr", například: "Aplikační trace Green (*.csv)|*csv".
        /// </summary>
        string[] ITraceReader.FileOpenFilters
        {
            get
            {
                return new string[]
                {
                    "Helios Apollo trace files (*.csv)|*.csv"
                };
            }
        }
        /// <summary>
        /// Metoda má za úkol podívat se na název souboru,
        /// a vyjádřit se k tomu, zda se bude chtít podívat i dovnitř souboru (tím, že nastaví args.CheckContent = true).
        /// Metoda by měla (pokud se tento TraceReader dokáže s názvem souboru vypořádat) rozebrat název souboru na jméno serveru, a oddělit i pořadové číslo z názvu.
        /// Metoda absolutně nemá za úkol otevírat soubor, pracuje jen s názvem.
        /// Pokud se metoda bude chtít podívat do souboru, nastaví args.CheckContent = true, a za chvíli se vyvolá další metoda: this.CheckContent().
        /// </summary>
        /// <param name="args">Data o souboru (název a výsledky této metody)</param>
        /// <returns></returns>
        void ITraceReader.CheckFileName(TraceReaderCheckNameArgs args)
        {
            // Nechci se dívat dovnitř (možná časem změním názor):
            args.CheckContent = false;

            // Přípona má být ".csv"
            System.IO.FileInfo fileInfo = args.FileInfo;
            if (!String.Equals(fileInfo.Extension, ".csv", StringComparison.OrdinalIgnoreCase))
                return;

            //MAF.APOLLO 
            // Jméno má začínat "ApolloTrace":
            string name = System.IO.Path.GetFileNameWithoutExtension(args.FileInfo.Name);
            if (!name.StartsWith("apollotrace", StringComparison.OrdinalIgnoreCase))
                return;

            // ..., a má mít určitou strukturu:
            string fileRoot, nameDate, fileNumber;
            if (!ParseFileName(args.FileInfo.Name, out fileRoot, out nameDate, out fileNumber))
                return;
            if (fileNumber.Length == 0)
                return;

            DateTime date = DetectNameDate(nameDate);
            if (date == DateTime.MinValue)
                return;

            // Vypadá to nadějně, určíme název serveru (adresář, základ, číslo dne):
            args.ServerName = System.IO.Path.Combine(fileInfo.Directory.FullName, fileRoot) + "_AplServer_" + date.ToString("yyyyMMdd");

            // Do argumentu vložím datum souboru odvozené z jeho záhlaví:
            args.TraceDate = date;

            // Šlo by to, podíváme se dovnitř:
            args.CheckContent = true;
        }
        /// <summary>
        /// Metoda je volaná tehdy, když tento reader se přihlásil ke čtení záhlaví souboru (v metodě CheckFileName, nastavil args.CheckContent = true).
        /// Tato metoda nemá za úkol číst data, ale podívat se dovnitř, a usoudit, zda my chceme tento soubor zpracovávat.
        /// Soubor je otevře, a je k němu přístup přes argument
        /// </summary>
        /// <param name="args"></param>
        void ITraceReader.CheckContent(TraceReaderCheckContentArgs args)
        {
            // Nechci soubor zpracovávat (možná časem změním názor):
            args.Priority = 0M;

			// Ověřím titulkový řádek:
			// Zatím umím jen tento řádek:
			string expected = "Date;Time;Offset;GCMemory;Stack;Assembly;Level;BSE;BEDepth;BEOffset;RtOffset;Login;SecureId;ThreadId;RoundtripId;PageId;Type;Method;Keyword;User0;User1;User2";
			string title = args.ReadFirstLine();
			if (title.Length != expected.Length) return;
			if (String.Compare(title, expected, StringComparison.CurrentCultureIgnoreCase) != 0) return;
			
            // Najdu začátek a konec dat v souboru:
            TimeRange contentTime = TimeRange.Empty;
            string line = args.FileStreamReader.ReadLine();
            if (line != null && line.Length >= 23)
                contentTime.Begin = MainInfo.ConvertDateTime(line);

            // Najdu poslední řádek, který obsahuje alespoň 19 prvků oddělených středníkem:
            line = args.ReadLastLine(";", 19);
            if (line != null && line.Length >= 23)
                contentTime.End = MainInfo.ConvertDateTime(line);
            else
                contentTime.End = contentTime.Begin;

            // Pokud jsem našel nějaká data, pak bych takový soubor mohl zpracovat:
            if (contentTime.Begin > DateTime.MinValue)
            {
                args.Priority = 1M;
                args.ContentTime = contentTime;
				args.TraceType = ApolloAplConstants.TraceType;
                args.ServerTimePriority = 2;                             // Tento server má druhý nejdůvěryhodnější čas
                this._PrepareContentElementTexts(args);
                this._PrepareContentTreePaths(args);
            }
        }
        /// <summary>
        /// Sestaví texty, které se budou vypisovat v elementu a v tooltipu
        /// </summary>
        /// <param name="args"></param>
        private void _PrepareContentElementTexts(TraceReaderCheckContentArgs args)
        {
            string pathDir1 = args.FileInfo.Directory.Name;         // Jméno prvního up adresáře
            string fileDir1 = Path.Combine(pathDir1, args.FileInfo.Name);  // jméno s prvním adresářem a s příponou
            string fileName = args.FileInfo.FullName;
            string fileRoot, nameDate, fileNumber;

            // Texty do elementu:
            if (ParseFileName(fileName, out fileRoot, out nameDate, out fileNumber))
                args.ElementTexts = new string[] 
                {
                    fileNumber,                                     // číslo
                    nameDate + "_" + fileNumber,                    // datum_číslo
                    Path.GetFileNameWithoutExtension(fileName),     // jméno bez adresáře a bez přípony
                    args.FileInfo.Name,                             // jméno bez adresáře s příponou
                    fileDir1,                                       // jméno s prvním adresářem a s příponou
                    fileName                                        // full filename
                };
            else
                args.ElementTexts = new string[] 
                {
                    Path.GetFileNameWithoutExtension(fileName),     // jméno bez adresáře a bez přípony
                    args.FileInfo.Name,                             // jméno bez adresáře s příponou
                    fileDir1,                                       // jméno s prvním adresářem a s příponou
                    fileName                                        // full filename
                };

            // Tooltip:
            args.ElementTooltip = "Vstupní soubor: " + fileDir1 + Environment.NewLine +
                "Obsahuje data za čas: " + args.ContentTime.StringShort;
        }
        /// <summary>
        /// Do argumentu připraví nody, které budeme později pravděpodobně používat:
        /// </summary>
        /// <param name="args"></param>
        private void _PrepareContentTreePaths(TraceReaderCheckContentArgs args)
        {
            // Plná cesta k nodu, absolutní (true), node = Uživatelé, graf = elementy se nenačítají, podřízené řádky se načítají dynamicky, třídí se podle textu:
            TreePath nodeUser = new TreePath(true);
            nodeUser.AddItem(CreateTreePathItemUsers());

            // Plná cesta k nodu, absolutní (true), node = Aplikační server, graf = elementy se nenačítají, podřízené řádky se načítají dynamicky, třídí se podle textu:
            TreePath nodeAplSrv = new TreePath(true);
            nodeAplSrv.AddItem(CreateTreePathItemAplServer());

            // Připravené dva nody dáme do TreePaths:
            args.TreePaths = new TreePath[] { nodeUser, nodeAplSrv };
        }
		/// <summary>
		/// Metoda je volaná pro zpracování celého obsahu dat.
		/// Veškerou podporu má v argumentu TraceReaderReadItemArgs args.
		/// Metoda si může libovolně zpracovat vstupující soubor 
		/// a vytvářet z něj data ve formě jednotlivých záznamů typu ITraceItem.
		/// Tyto záznamy předává ke zpracování do datového systému pomocí volání metody argumentu ProcessItem(ITraceItem item).
		/// Metoda takto může vynechat některé věty (nedávat je ke zpracování), anebo některé věty zpracovat více způsoby
		/// (například z jedné věty trace vytvoří dvě položky: jednu ktertá představuje aktivitu uživatele, a druhou která popisuje stav paměti).
		/// Tím, že zpracování probíhá v jedné metodě, si reader může udržovat kontinuální povědomí o načítaných datech, 
		/// a například o stavu paměti posílat položky při změně hodnoty (není třeba posílat 1000 x jednu hodnotu stavu paměti).
		/// </summary>
		/// <param name="args"></param>
		void ITraceReader.ProcessContent(TraceReaderProcessContentArgs args)
		{
			this._ProcessContent(args);
		}
        /// <summary>
        /// Tato metoda má za úkol vytvořit a vrátit logickou událost na základě fyzických dat načtených z trace
        /// (před malinkou chvilkou je objekt osobně načetl a vrátil v metodě ReadItem()).
        /// Pokud se vrátí null, pak tato položka trace nemá být zakládána do evidence.
        /// </summary>
        IEventItem ITraceReader.CreateIEventItem(IEventEdge data)
        {
            return _CreateIEventItem(data);
        }
        /// <summary>
        /// Metoda, která má za úkol vytvořit celou plně definovanou cestu k nodu.
        /// Tato metoda se volá zcela výjimečně - tehdy, když tento objekt předepisuje nějakou TargetPath, která dosud neexistuje.
        /// Pak si jádro vyžádá plnou cestu k nodu, včetně textů a definice všech nodů.
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        void ITraceReader.CreateFullTreePath(TraceReaderCreatePathArgs args)
        {
            this.CreateFullTreePath(args);
        }
		void IDisposable.Dispose()
        {
        }
        #endregion
        #region Rozklad názvu souboru na prvky
        /// <summary>
        /// Ze jména souboru (adresář není třeba zadávat, ale nevadí) najde základ, datovou část a číslo souboru
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="fileRoot"></param>
        /// <param name="nameDate"></param>
        /// <param name="fileNumber"></param>
        internal static bool ParseFileName(string fileName, out string fileRoot, out string nameDate, out string fileNumber)
        {
            fileRoot = null;
            nameDate = null;
            fileNumber = null;
            string name = System.IO.Path.GetFileNameWithoutExtension(fileName);
            int x;

            // Na konci je číslo:
            x = name.LastIndexOf("_");
            if (x <= 1 || x >= name.Length) return false;
            fileNumber = name.Substring(x + 1);
            name = name.Substring(0, x);

            // Před číslem je datum:
            x = name.LastIndexOf("_");
            if (x <= 1 || x >= name.Length) return false;
            nameDate = name.Substring(x + 1);
            fileRoot = name.Substring(0, x);

            return true;
        }
        /// <summary>
        /// Z textu s datem v názvu souboru (text ve formě "YYMMDD") určí a vrátí datum trace.
        /// Pokud nejde určit, vrací MinValue.
        /// </summary>
        /// <param name="nameDate"></param>
        /// <returns></returns>
        internal static DateTime DetectNameDate(string nameDate)
        {
            DateTime result = DateTime.MinValue;
            if (!String.IsNullOrEmpty(nameDate) && nameDate.Length == 6)
            {
                int year, month, day;
                if (Int32.TryParse(nameDate.Substring(0, 2), out year) &&
                    Int32.TryParse(nameDate.Substring(2, 2), out month) &&
                    Int32.TryParse(nameDate.Substring(4, 2), out day))
                {
                    if (month > 0 && month <= 12 &&
                        day > 0 && day <= 31)
                    {
                        try
                        {
                            result = new DateTime(2000 + year, month, day);
                        }
                        catch
                        {
                            result = DateTime.MinValue;
                        }
                    }
                }
            }
            return result;
        }
        #endregion
		#region Fyzické načítání obsahu souboru a zpracování na položky ITraceItem
		/// <summary>
		/// Zpracuje data do jednotlivých položek
		/// </summary>
		/// <param name="args"></param>
		private void _ProcessContent(TraceReaderProcessContentArgs args)
		{
			TraceServerPersistData persistData = TraceServerPersistData.GetFrom(args.Server.Tag);
			try
			{
				this.StringHeap = args.Main.StringHeap;
				this.TitleLine = args.FileStreamReader.ReadLine();
				this.LineNumber = 0;

				while (!args.FileStreamReader.EndOfStream)
				{
					string lineText = args.FileStreamReader.ReadLine();
					this.LineNumber++;

					// Vytvořit a zpracovat položku (jedna položka může obsahovat mnoho různých cílových nodů = řádků, kde bude zobrazena):
					TraceItem traceItem = TraceItem.CreateItem(args.Main.StringHeap, (this.LineNumber + 1), lineText, ref persistData);
					if (traceItem != null)
					{
						args.ProcessItem(traceItem);
					}
				}
			}
			finally
			{
                if (args.Server != null)
    				args.Server.Tag = persistData;
				this.StringHeap = args.Main.StringHeap;
			}
		}
		/// <summary>
		/// Reference na zásobník textů
		/// </summary>
		protected StringHeap StringHeap { get; set; }
		protected string TitleLine { get; set; }
		protected int LineNumber { get; set; }
		#endregion
		#region Podpora pro tvorbu nodů grafu (řádky: klíče, názvy, definice grafiky a chování řádku)
        /// <summary>
        /// Sestavení plné cesty k nodu na základě jejího stringu
        /// </summary>
        /// <param name="args"></param>
        protected void CreateFullTreePath(TraceReaderCreatePathArgs args)
        {
            // Připravím si polotovar (tam se mi rozeberou jednotlivé složky cesty):
            TreePath input = new TreePath(args.Path);
            List<TreePathItem> items = input.Items;

            // Připravím si výsledek:
            TreePath result = new TreePath(input.AbsolutePath);
            string status = (input.AbsolutePath ? "root" : "node");           // Jakou část cesty budu nyní zpracovávat? Dynamicky se posouvá podle toho, jak se posouvám na konkrétní cestě
            foreach (TreePathItem item in items)
            {
                switch (status)
                {
                    case "root":
                        // Řeším node v root úrovni:
                        // Podle klíče položky určím, co v ní je, a jakého typu bude příští položka:
                        switch (item.Key)
                        {
                            case ApolloAplConstants.NodeUser:
                                result.AddItem(TraceReader.CreateTreePathItemUsers());
                                status = "user";               // V příští úrovni cesty (další TreePathItem item in items) očekávám konkrétního uživatele
                                break;
                            case ApolloAplConstants.NodeAplServer:
                                result.AddItem(TraceReader.CreateTreePathItemAplServer());
                                status = "apls";               // V příští úrovni cesty budou položky aplikačního serveru (SQL, Memory)
                                break;
                        }
                        break;
                    case "user":
                        // V této úrovni bude login a jméno uživatele:
                        result.AddItem(TraceReader.CreateTreePathItemUser(item.Key));
                        status = "detail";                     // V příští úrovni cesty budou detailní položky jednotlivého uživatele
                        break;
                    case "detail":
                    case "subdetail":
                        // Úroveň pod konkrétním uživatelem = detaily:
                        result.AddItem(TraceReader.CreateTreePathItemDetail(item.Key));
                        status = "subdetail";                  // V příští úrovni cesty očekávám poddetaily, typicky pod nodem kernel očekávám info
                        break;
                    case "apls":
                        // Zde jsme v některém řádku podřízeném pod "Aplikační server". Může to být Memory nebo Sql:
                        switch (item.Key)
                        {
                            case ApolloAplConstants.SubNodeAplMemory:
                                result.AddItem(TraceReader.CreateTreePathItemMemory(item.Key));
                                status = "none";              // V příští úrovni cesty neočekávám nic dalšího
                                break;
                            case ApolloAplConstants.SubNodeAplSql:
                                result.AddItem(TraceReader.CreateTreePathItemAplSql(item.Key));
                                status = "apspid";            // V příští úrovni cesty očekávám konkrétní číslo procesu SQL
                                break;
                            case ApolloAplConstants.SubNodeAplSystem:
                                result.AddItem(TraceReader.CreateTreePathItemAplSystem(item.Key));
                                status = "aplsystem";         // V příští úrovni cesty očekávám nody info, warning, error
                                break;
                        }
                        break;
                    case "apspid":
                        result.AddItem(TraceReader.CreateTreePathItemAplSqPid(item.Key));
                        status = "none";                      // V příští úrovni cesty neočekávám nic dalšího
                        break;
                    case "aplsystem":
                        result.AddItem(TraceReader.CreateTreePathItemDetail(item.Key));
                        status = "none";                      // V příští úrovni cesty neočekávám nic dalšího
                        break;
                    case "node":
                        // Na vstupu je obecný node, ne Root, takže se musím orientovat podle názvu nodu:
                        switch (item.Key)
                        {
                            case ApolloAplConstants.SubNodeUserKernel:
                            case ApolloAplConstants.SubNodeUserInstance:
                            case ApolloAplConstants.SubNodeUserCowley:
                            case ApolloAplConstants.SubNodeUserSql:
                                // Úroveň pod konkrétním uživatelem = detaily:
                                result.AddItem(TraceReader.CreateTreePathItemDetail(item.Key));
                                status = "subdetail";                  // V příští úrovni cesty očekávám poddetaily, typicky pod nodem kernel očekávám info
                                break;
                            default:
                                result.AddItem(TraceReader.CreateTreePathItemDefault(item.Key));
                                break;
                        }
                        break;
                    default:
                        result.AddItem(TraceReader.CreateTreePathItemDefault(item.Key));
                        break;
                }
            }
            args.TreePath = result;
        }
        /// <summary>
		/// Vrátí definici pro položku grafu s textem "Uživatelé", která se zobrazuje v root úrovni grafu.
		/// </summary>
		/// <returns></returns>
		internal static TreePathItem CreateTreePathItemUsers()
		{
			return new TreePathItem(ApolloAplConstants.NodeUser, "Uživatelé",
				GraphRowDeclarationCls.StandardMultiTask(14, 0.1M, RowProperties.DynamicSubNodeLoading | RowProperties.ChildSortedText));
		}
		/// <summary>
		/// Vrátí definici pro položku grafu s textem "Aplikační server", která se zobrazuje v root úrovni grafu.
		/// </summary>
		/// <returns></returns>
		internal static TreePathItem CreateTreePathItemAplServer()
        {
            return new TreePathItem(ApolloAplConstants.NodeAplServer, "Aplikační server", 
                GraphRowDeclarationCls.StandardMultiTask(14, 0.1M, RowProperties.DynamicSubNodeLoading | RowProperties.ChildSortedText));
        }
		/// <summary>
		/// Vrátí definici pro položku grafu pro konkrétního uživatele, která se zobrazuje jako podřízená v položce Uživatelé
		/// </summary>
		/// <returns></returns>
		internal static TreePathItem CreateTreePathItemUser(string key)
		{
			string name = key;
			return new TreePathItem(key, name,
				GraphRowDeclarationCls.StandardMultiTask(14, 0.1M, RowProperties.PreLoadingElements | RowProperties.DynamicElementsLoading | RowProperties.DynamicSubNodeLoading | RowProperties.ChildSortedKey));
		}
		/// <summary>
		/// Vrátí definici pro položku grafu pro konkrétního uživatele, která se zobrazuje jako podřízená v položce Uživatelé
		/// </summary>
		/// <returns></returns>
		internal static TreePathItem CreateTreePathItemDetail(string key)
		{
			string name = key;
            int lineHeight = 14;
            int? lineMaxHeight = null;
			switch (key)
			{
				case ApolloAplConstants.SubNodeUserSql:
					name = "SQL příkazy";
					break;
				case ApolloAplConstants.SubNodeUserCowley:
					name = "Biz funkce";
                    lineHeight = 12;
					break;
				case ApolloAplConstants.SubNodeUserInstance:
					name = "Biz záznam";
                    lineMaxHeight = 240;
					break;
				case ApolloAplConstants.SubNodeUserKernel:
					name = "Jádro";
                    lineHeight = 12;
					break;
                case ApolloAplConstants.SubNodeAplSql:
                    name = "Souhrn SQL příkazů";
					break;
                case ApolloAplConstants.SubNodeInfo:
                    name = "Sys.Info";
                    break;
                case ApolloAplConstants.SubNodeWarning:
                    name = "Sys.Warning";
                    break;
                case ApolloAplConstants.SubNodeError:
                    name = "Sys.Error";
                    break;
            }
			GraphRowDeclarationCls dcl = GraphRowDeclarationCls.StandardMultiTask(lineHeight, 0.1M, RowProperties.PreLoadingElements | RowProperties.DynamicElementsLoading);
            dcl.RowHeightTotalMaxPx = lineMaxHeight;
			dcl.ShowPrevNextIcon = true;
			return new TreePathItem(key, name, dcl);
		}
		/// <summary>
		/// Vrátí definici pro položku grafu SouhrnSQL, která se zobrazuje jako podřízená v položce Aplikační server.
        /// Tato položka (řádek) nezobrazuje elementy, ale slouží jako root pro jednotlivé SQL procesy.
		/// </summary>
		/// <returns></returns>
        internal static TreePathItem CreateTreePathItemAplSql(string key)
		{
			string name = "SQL procesy (SPID)";
            GraphRowDeclarationCls dcl = GraphRowDeclarationCls.StandardMultiTask(14, 0.1M, RowProperties.DynamicSubNodeLoading | RowProperties.DynamicElementsLoading | RowProperties.ChildSortedKey);
			return new TreePathItem(key, name, dcl);
		}
        /// <summary>
        /// Vrátí definici pro položku grafu SouhrnSQL.Proces PID, která se zobrazuje jako podřízená v položce Aplikační server/Souhrn SQL příkazů
        /// </summary>
        /// <returns></returns>
        internal static TreePathItem CreateTreePathItemAplSqPid(string key)
        {
            string name = /* "Sql proces " + */ key;
			GraphRowDeclarationCls dcl = GraphRowDeclarationCls.StandardMultiTask(12, 0.1M, RowProperties.PreLoadingElements | RowProperties.DynamicElementsLoading);
            dcl.RowHeightTotalMinPx = 16;
            dcl.RowHeightTotalMaxPx = 200;
            dcl.ShowPrevNextIcon = true;
            return new TreePathItem(key, name, dcl);
        }
        /// <summary>
		/// Vrátí definici pro položku grafu zobrazující paměť.
		/// </summary>
		/// <returns></returns>
		internal static TreePathItem CreateTreePathItemMemory(string key)
		{
			string name = key;
			GraphRowDeclarationCls dcl = GraphRowDeclarationCls.StandardLine(14, 0.1M, RowProperties.PreLoadingElements | RowProperties.DynamicElementsLoading);
			dcl.ShowPrevNextIcon = true;
			return new TreePathItem(key, name, dcl);
		}
		/// <summary>
		/// Vrátí definici pro položku grafu SouhrnSQL, která se zobrazuje jako podřízená v položce Aplikační server.
        /// Tato položka (řádek) nezobrazuje elementy, ale slouží jako root pro jednotlivé SQL procesy.
		/// </summary>
		/// <returns></returns>
        internal static TreePathItem CreateTreePathItemAplSystem(string key)
		{
			string name = "Systémové informace";
            GraphRowDeclarationCls dcl = GraphRowDeclarationCls.StandardMultiTask(14, 0.1M, RowProperties.DynamicSubNodeLoading | RowProperties.DynamicElementsLoading | RowProperties.ChildSortedKey);
			return new TreePathItem(key, name, dcl);
		}
		/// <summary>
		/// Vrátí definici pro položku grafu defaultní, bez podrobnější znalost věci
		/// </summary>
		/// <returns></returns>
		internal static TreePathItem CreateTreePathItemDefault(string key)
		{
			string name = key;
			return new TreePathItem(key, name,
				GraphRowDeclarationCls.StandardMultiTask(14, 0.1M, RowProperties.PreLoadingElements | RowProperties.DynamicElementsLoading | RowProperties.DynamicSubNodeLoading));
		}
		#endregion
		#region Vytvoření logické události IEventItem na základě dat z trace IEventEdge
        /// <summary>
        /// Tato metoda má za úkol vytvořit a vrátit logickou událost na základě fyzických dat načtených z trace
        /// (před malinkou chvilkou je objekt osobně načetl a vrátil v metodě ReadItem()).
        /// Pokud se vrátí null, pak tato položka trace nemá být zakládána do evidence.
        /// </summary>
		/// <param name="data"></param>
        /// <returns></returns>
        private IEventItem _CreateIEventItem(IEventEdge data)
        {
            if (data == null)
				return null;

            TraceItem item = data as TraceItem;
			if (item == null)
                Msg.Error("Chyba: pro vytvoření objektu IEventItem byl do readeru Apollo NorisASrvTraceReader předán nesprávný objekt.\r\nOčekávaný typ: {0}, chybný typ: {1}", typeof(TraceItem).Name, data.GetType().Name);

			EventItem thing = EventItem.CreateEventItem(this.StringHeap, item);

			return thing;
        }
        #endregion
		#region IColorDeclarator Members : deklarace konkrétních barev pro názvy ColorGroups
		void IColorDeclarator.ColorDeclare(ColorDeclaratorArgs args)
		{
			// INSTANCE: _InitDataStores, _InitDataStoresGetSelects, GetInstance, Init, NewEntry, RecountEntries, Reset, Retrieve, SetFolder, Update
            args.Append(ApolloAplConstants.ColorGroupInstance, Color.LightCyan);
			args.Append(ApolloAplConstants.ColorGroupInstanceDot + "InitDatastores", Color.Lavender);
			args.Append(ApolloAplConstants.ColorGroupInstanceDot + "InitDatastoresGetSelects", Color.Lavender);
			args.Append(ApolloAplConstants.ColorGroupInstanceDot + "GetInstance", Color.Lavender);
			args.Append(ApolloAplConstants.ColorGroupInstanceDot + "Init", Color.PowderBlue);
			args.Append(ApolloAplConstants.ColorGroupInstanceDot + "NewEntry", Color.Orange);
			args.Append(ApolloAplConstants.ColorGroupInstanceDot + "RecountEntries", Color.Orange);
			args.Append(ApolloAplConstants.ColorGroupInstanceDot + "Reset", Color.PowderBlue);
			args.Append(ApolloAplConstants.ColorGroupInstanceDot + "Retrieve", Color.LimeGreen);
			args.Append(ApolloAplConstants.ColorGroupInstanceDot + "SetFolder", Color.Lavender);
			args.Append(ApolloAplConstants.ColorGroupInstanceDot + "Update", Color.Tomato);

			// IOMy:
			args.Append(ApolloAplConstants.ColorGroupInstanceDot + "Recordrules", Color.MediumPurple);
			args.Append(ApolloAplConstants.ColorGroupInstanceDot + "Recordrules.Reset", Color.FromArgb(181, 130, 238));
			args.Append(ApolloAplConstants.ColorGroupInstanceDot + "Recordrules.FlowDefaults", Color.Plum);
			args.Append(ApolloAplConstants.ColorGroupInstanceDot + "Recordrules.TestAttr", Color.Pink);
			args.Append(ApolloAplConstants.ColorGroupInstanceDot + "Recordrules.TestSave", Color.Thistle);

			// SQL:
			args.Append("Sql", Color.RosyBrown);
			args.Append("Sql.Fill", Color.LimeGreen);
			args.Append("Sql.ExecuteScalar", Color.MediumSpringGreen);
			args.Append("Sql.GetValue", Color.LightGreen);
			args.Append("Sql.ExecuteNonquery", Color.OrangeRed);
			args.Append("Sql.DoUpdate", Color.Tomato);
			// Tyto položky jsou společné s Readerem Noris.Tools.TraceVisualiser.Green.SqlServer.TraceReader :
			args.Append("Sql.Select", Color.LimeGreen);
			args.Append("Sql.Insert", Color.Tomato);
			args.Append("Sql.Update", Color.Moccasin);
			args.Append("Sql.Delete", Color.Violet);
			args.Append("Sql.Exec", Color.LightBlue);

			// Transakce mají své názvy:
			args.Append("Trans", Color.PapayaWhip);
			args.Append("Trans.Begin", Color.PapayaWhip);
			args.Append("Trans.Commit", Color.FromArgb(189, 234, 183));
			args.Append("Trans.Rollback", Color.MistyRose);
			args.Append("Trans.End", Color.Silver);

			// NrsCowley: například specificky podle namespace:
            args.Append(ApolloAplConstants.ColorGroupCowley, Color.GreenYellow);
            args.Append(ApolloAplConstants.ColorGroupCowleyDot + "Noris", Color.GreenYellow);
			args.Append(ApolloAplConstants.ColorGroupCowleyDot + "Noris.LCS", Color.Orange);
            //args.Append(NorisAplConstants.ColorGroupCowleyDot + "Noris.LCS.Accounting", Color.LightSkyBlue);
            //args.Append(NorisAplConstants.ColorGroupCowleyDot + "Noris.LCS.Admin", Color.DarkKhaki);
            //args.Append(NorisAplConstants.ColorGroupCowleyDot + "Noris.LCS.Banking", Color.Cyan);
            //args.Append(NorisAplConstants.ColorGroupCowleyDot + "Noris.LCS.Base", Color.BurlyWood);
            //args.Append(NorisAplConstants.ColorGroupCowleyDot + "Noris.LCS.DepreciationAR", Color.DarkOrange);
            //args.Append(NorisAplConstants.ColorGroupCowleyDot + "Noris.LCS.Economia", Color.DarkSeaGreen);
            //args.Append(NorisAplConstants.ColorGroupCowleyDot + "Noris.LCS.Finance", Color.DeepSkyBlue);
            //args.Append(NorisAplConstants.ColorGroupCowleyDot + "Noris.LCS.FixedAssets", Color.Gainsboro);
            //args.Append(NorisAplConstants.ColorGroupCowleyDot + "Noris.LCS.HumanResources", Color.FloralWhite);
            //args.Append(NorisAplConstants.ColorGroupCowleyDot + "Noris.LCS.Invoice", Color.Honeydew);
            //args.Append(NorisAplConstants.ColorGroupCowleyDot + "Noris.LCS.InvoiceOut", Color.Honeydew);
            //args.Append(NorisAplConstants.ColorGroupCowleyDot + "Noris.LCS.JobOrder", Color.Gold);
            //args.Append(NorisAplConstants.ColorGroupCowleyDot + "Noris.LCS.Manufacturing", Color.LightSkyBlue);
            //args.Append(NorisAplConstants.ColorGroupCowleyDot + "Noris.LCS.PrimaryVATDocument", Color.Coral);
            //args.Append(NorisAplConstants.ColorGroupCowleyDot + "Noris.LCS.VIS", Color.LawnGreen);
            //args.Append(NorisAplConstants.ColorGroupCowleyDot + "Noris.LCS.Warehouse", Color.Goldenrod);
            //args.Append(NorisAplConstants.ColorGroupCowleyDot + "Noris.LCS.Workflow", Color.Lavender);
            //args.Append(NorisAplConstants.ColorGroupCowleyDot + "Noris.ASV", Color.Aquamarine);
            //args.Append(NorisAplConstants.ColorGroupCowleyDot + "Noris.Gatema", Color.LightPink);

			// Jádro chce taky barvy:
            //  "RunThread" || this.Method == "Suspend" || this.Method == "ResumeThread"
			args.Append(ApolloAplConstants.ColorGroupKernel, Color.LightSteelBlue);
			args.Append(ApolloAplConstants.ColorGroupKernelDot + "Thread", Color.LightSkyBlue);
            args.Append(ApolloAplConstants.ColorGroupKernelDot + "Thread.ProcessRequest", Color.SkyBlue);
            args.Append(ApolloAplConstants.ColorGroupKernelDot + "Thread.RunThread", Color.Lime);
            args.Append(ApolloAplConstants.ColorGroupKernelDot + "Thread.Suspend", Color.LightCoral);
            args.Append(ApolloAplConstants.ColorGroupKernelDot + "Thread.ResumeThread", Color.GreenYellow);
		}
		#endregion
	}
    /// <summary>
    /// Instance této třídy persistuje v ServerInfo.Tag mezi jednotlivými soubory Trace
    /// (tedy mezi jednotlivými voláními metody this._ProcessContent).
    /// Tím je umožněno předávat si kontinuální data do načítání dalšího souboru.
    /// </summary>
    internal class TraceServerPersistData
    {
        internal static TraceServerPersistData GetFrom(object tag)
        {
            TraceServerPersistData data = null;
            if (tag != null && tag is TraceServerPersistData)
                data = tag as TraceServerPersistData;
            if (data == null)
                data = new TraceServerPersistData();
            return data;
        }
        /// <summary>
        /// Stav GCMemory po poslední změně, i kdyby to bylo v minulém souboru
        /// </summary>
        internal int LastGcMemoryChangeValue { get; set; }
        /// <summary>
        /// Datum poslední změny hodnoty GCMemory, i kdyby to bylo v minulém souboru
        /// </summary>
        internal DateTime LastGcMemoryChangeDate { get; set; }
    }
    #endregion
    #region TraceItem : fyzická data jedné položky (opis položky a ukazatel jejího cíle), načtené ze souboru Aplikační trace Noris. Zde se určují cílové Node pro událost.
    /// <summary>
    /// TraceItem : fyzická data jedné položky, načtené ze souboru Aplikační trace Noris
    /// </summary>
    public class TraceItem : IEventEdge
    {
        #region Konstrukce, parsování
		private TraceItem(StringHeap stringHeap)
        {
			this.StringHeap = stringHeap;
		}
		private StringHeap StringHeap;
        /// <summary>
        /// Vytvoří a vrátí data.
        /// Pokud data nejsou korektně načtena (nesprávný řádek), vrátí null.
        /// </summary>
		/// <param name="stringHeap"></param>
        /// <param name="lineNumber"></param>
        /// <param name="lineText"></param>
		/// <param name="persistData"></param>
        /// <returns></returns>
        internal static TraceItem CreateItem(StringHeap stringHeap, int lineNumber, string lineText, ref TraceServerPersistData persistData)
        {
            TraceItem item = new TraceItem(stringHeap);
            item.LineNumber = lineNumber;
            item.ParseLine(lineText);
            if (!item._LineIsParsed) return null;             // odbočka při nekorektním tvaru řádku
            item.DetectFilter();
            item.DetectMemoryChange(ref persistData);
            //item.DetectTargets();
            return item;
        }
        /// <summary>
        /// Detekuje změnu hodnoty paměti. Při změně si nastaví příznak, a následně připraví Target i do řádku Memory.
        /// </summary>
		/// <param name="persistData"></param>
        internal void DetectMemoryChange(ref TraceServerPersistData persistData)
        {
            this.MemoryChanged = (this.GCMemory != persistData.LastGcMemoryChangeValue);
            if (this.MemoryChanged)
            {
                persistData.LastGcMemoryChangeValue = this.GCMemory;
                persistData.LastGcMemoryChangeDate = this.Time;
            }
        }
        /// <summary>
        /// Parsování textu řádku do proměnných.
        /// </summary>
        private void ParseLine(string lineText)
        {
            if (String.IsNullOrEmpty(lineText)) return;

            // Soubor o velikosti 26,5 MB (27 853 598 Bytes) s počtem 65 001 řádků:
            // Čtení souboru:                     0.424 sec
            // + dělení pomocí String.Split()     0.625 sec
            // + parsování data a času            0.752 sec
            // + parsování až do _BSE             0.980 sec
            // + parsování kompletní              1.250 sec
            //   což považuji za OK.

            string[] items = lineText.Split(';');
            int length = items.Length;
            if (length < 19)
                return;

            // Parsování:
            this._Time = MainInfo.ConvertDateTime(items[0] + ";" + items[1]);
            Int32.TryParse(items[2], out this._Offset);
            Int32.TryParse(items[3], out this._GCMemory);
            this._Stack = this.StringHeap.GetInt32(items[4]);
            this._Assembly = this.StringHeap.GetInt32(items[5]);
            this._Level = GetIWEType(items[6]);
            this._BSE = GetBSEType(items[7]);
			if (items[8] == "" || items[8] == "0")
				this._BEDepth = 0;       // V trace se u systémových událostí objevuje BEDepth = "" (nebo "0"?), což tam znamená "nic". Ale pro nás "nic" = null.
			else
	            this._BEDepth = this.StringHeap.GetInt32(items[8]);
            Int32.TryParse(items[9], out this._BEOffset);
            Int32.TryParse(items[10], out this._RtOffset);
            this._Login = this.StringHeap.GetInt32(items[11]);
            this._SecureId = this.StringHeap.GetInt32(items[12]);
            Int32.TryParse(items[13], out this._ThreadId);
            Int32.TryParse(items[14], out this._RoundtripId);
            Int32.TryParse(items[15], out this._PageId);
            this._Type = this.StringHeap.GetInt32(items[16]);
            this._Method = this.StringHeap.GetInt32(items[17]);
            this._Keyword = this.StringHeap.GetInt32(items[18]);
            
            // UserData:
            if (length >= 20)
            {
				int userLength = length - 19;
					
                this._UserLength = userLength;
                this._User = new Int32[userLength];
				for (int i = 0; i < userLength; i++)
					this._User[i] = this.StringHeap.GetInt32(items[19 + i]);
            }
            else
            {
                this._UserLength = 0;
                this._User = null;
            }

            // Dokončení:
            this._LineIsParsed = true;
        }
        /// <summary>
        /// Begin / End / System ?
        /// </summary>
        protected LineBSE GetBSEType(string bse)
        {
            switch (bse)
            {
                case "B": return LineBSE.Begin;
                case "S": return LineBSE.System;
                case "E": return LineBSE.End;
            }
            return LineBSE.None;
        }
        /// <summary>
        /// Information / Warning / Error ?
        /// </summary>
        protected LineIWE GetIWEType(string iwe)
        {
            switch (iwe)
            {
                case "I": return LineIWE.Information;
                case "W": return LineIWE.Warning;
                case "E": return LineIWE.Error;
            }
            return LineIWE.None;
        }
		// Proměnné typované Int32 obsahují pointer na string do StringHeap:
        private DateTime _Time;
        private int _Offset;
        private int _GCMemory;
		private Int32 _Stack;
		private Int32 _Assembly;
        private LineIWE _Level;
        private LineBSE _BSE;
		private Int32 _BEDepth;
        private int _BEOffset;
        private int _RtOffset;
		private Int32 _Login;
		private Int32 _SecureId;
        private int _ThreadId;
        private int _RoundtripId;
        private int _PageId;
        private Int32 _Type;
        private Int32 _Method;
		private Int32 _Keyword;
        private int _UserLength;
		private Int32[] _User;

        private bool _LineIsParsed;
        #endregion
        #region IEventPointer, IDisposable Members
		/// <summary>
		/// Klíč procesu. Nemůže být prázdný (pak se event nikam nezařadí; ledaže bychom povolili jeden empty proces - ale zatím ne).
		/// </summary>
        string IEventPointer.ProcessKey { get { return this.SecureId; } }
		/// <summary>
		/// Klíč události. Může být empty, pak ale nebude možno do této události přidávat další data o události (IEventEdge), 
		/// protože je nebude možno spárovat.
		/// </summary>
        string IEventPointer.EventKey { get { return this.BEDepth; } }
        /// <summary>
        /// Pořadové číslo události v rámci trace souboru, 
        /// slouží k přesnému třídění pořadí událostí tehdy, když čas dvou sousedních událostí je zcela shodný.
        /// Ono za 1 milisekundu lze stihnout desítky věcí...
        /// </summary>
        int IEventPointer.LineNumber { get { return this.LineNumber; } }
        void IDisposable.Dispose()
        {
            this._Stack = 0;
            this._Assembly = 0;
            this._BEDepth = 0;
            this._Login = 0;
            this._SecureId = 0;
            this._Type = 0;
            this._Method = 0;
            this._Keyword = 0;
            this._User = null;

            this.FilterKeyword = null;
        }
        #endregion
        #region Property řádku - načtené z fyzického podkladu
        public int LineNumber { get; private set; }
        public DateTime Time { get { return _Time; } }
        public int Offset { get { return _Offset; } }
        public int GCMemory { get { return _GCMemory; } }
        public string Stack { get { return this.StringHeap.GetString(_Stack); } }
		internal Int32 StackInt32 { get { return _Stack; } }
		public string Assembly { get { return this.StringHeap.GetString(_Assembly); } }
		internal Int32 AssemblyInt32 { get { return _Assembly; } }
		public LineIWE Level { get { return _Level; } }
        public LineBSE BSE { get { return _BSE; } }
        public string BEDepth { get { return this.StringHeap.GetString(_BEDepth); } }
		internal Int32 BEDepthInt32 { get { return _BEDepth; } }
		public int BEOffset { get { return _BEOffset; } }
        public int RtOffset { get { return _RtOffset; } }
        public string Login { get { return this.StringHeap.GetString(_Login); } }
		internal Int32 LoginInt32 { get { return _Login; } }
		public string SecureId { get { return this.StringHeap.GetString(_SecureId); } }
		internal Int32 SecureIdInt32 { get { return _SecureId; } }
		public int ThreadId { get { return _ThreadId; } }
        public int RoundtripId { get { return _RoundtripId; } }
        public int PageId { get { return _PageId; } }
        public string Type { get { return this.StringHeap.GetString(_Type); } }
		internal Int32 TypeInt32 { get { return _Type; } }
		public string Method { get { return this.StringHeap.GetString(_Method); } }
		internal Int32 MethodInt32 { get { return _Method; } }
		public string Keyword { get { return this.StringHeap.GetString(_Keyword); } }
		internal Int32 KeywordInt32 { get { return _Keyword; } }
		public int UserLength { get { return _UserLength; } }
        public Int32[] UserDataInt32 { get { return _User; } }
		public string User(int index)
		{
			return this.StringHeap.GetString(_User[index]);
		}
        #endregion
        #region Další property, určující zatřídění řádku
        /// <summary>
        /// Příznak, že tato položka má jinou hodnotu paměti, než předešlá událost v řadě.
        /// </summary>
        public bool MemoryChanged { get; protected set; }
        /// <summary>
        /// Klíč skupiny událostí, které lze mezi sebou mergovat.
        /// </summary>
		public string MergeKey { get { return this.StringHeap.GetString(MergeKeyInt32); } }
		internal Int32 MergeKeyInt32 { get; set; }
        #endregion
        #region Podpora pro filtrování
        /// <summary>
        /// Klíčové slovo pro filtraci událostí.
        /// Uživatel může ve vlastnostech projektu určit, jaké události chce načítat (specifikuje je jako soupis stringových konstant).
        /// Každá událost pak musí uvádět svoje klíčové slovo, které je při načítání dat porovnáváno se zadaným filtrem, a pouze vyhovující události se přebírají do systému.
        /// Reader události nefiltruje, to zajišťuje datový systém.
        /// Názvy položek filtru jsou uvedeny na třídě TraceDocument jako konstanty začínající slovem FILTER_*.
        /// Speciální význam mají hodnoty: 
        /// a) hodnota null nebo empty: událost bude zobrazena jen v případě, kdy uživatel zaškrtne VŠE
        /// b) hodnota * bude zobrazena vždy, i když by uživatel nic nepovolil
        /// </summary>
        public string FilterKeyword { get; set; }
        /// <summary>
        /// Určí hodnotu FilterKeyword
        /// </summary>
        protected void DetectFilter()
        {
            string keywordUppered = this.Keyword.Trim().ToUpper();
            string methodUppered = this.Method.Trim().ToUpper();

            // Systémové události:
            if (this.BSE == LineBSE.System && keywordUppered != "TRANS")
            {
                switch (this.Level)
                {
                    case LineIWE.Error:
                        this.FilterKeyword = TraceDocument.FILTER_SYSMSG_ERROR;
                        break;
                    case LineIWE.Warning:
                        this.FilterKeyword = TraceDocument.FILTER_SYSMSG_WARNING;
                        break;
                    case LineIWE.Information:
                        this.FilterKeyword = TraceDocument.FILTER_SYSMSG_INFO;
                        break;
                }
                return;
            }

            // Párové události + TRANS:
            switch (keywordUppered)
            {
                // --- Keyword -------------- Method -------
                //MAF.APOLLO+
                case "SYSTEMERROR":
                case "USERERROR":
                case "SYSTEMUSERERROR":
                    this.FilterKeyword = TraceDocument.FILTER_FORMAT;
                    break;
                //MAF.APOLLO-
                case "ATTEMPT":
                    this.FilterKeyword = TraceDocument.FILTER_FORMAT;
                    break;
                case "AFTER":             // ItemChanged
                case "BEFORE":
                case "ITEMCHANGEDBEFORE":
                    this.FilterKeyword = TraceDocument.FILTER_INSTANCE_CHANGE;
                    break;
                case "AUDITOR":           // Begin, End
                    this.FilterKeyword = TraceDocument.FILTER_FORMAT;
                    break;
                case "CACHE":
                    this.FilterKeyword = TraceDocument.FILTER_CACHE;
                    break;
                case "CLIENTERROR":
                    this.FilterKeyword = TraceDocument.FILTER_FORMS;
                    break;
                case "COWLEY":            // _GetCowley, _Run1_nonvisualPart, _Run2_progressbarPart, _Run3_rundonePart, _Run5_invokePart
                    this.FilterKeyword = TraceDocument.FILTER_COWLEY;
                    break;
                case "DATA":
                case "DATACONTAINER":     // _Retrieve, Invalidate, SetInvalidate, 
                case "DATAFORM":          // 
                case "DATASTORE":
                case "ERROR":
                case "EXTENSIONS":
                case "FORMAT":
                case "FORMDETAIL":
                case "GETINFO":
                case "GETTEMPLATE":
                    this.FilterKeyword = TraceDocument.FILTER_FORMAT;
                    break;
                case "INSTANCE":          // _InitDataStores, _InitDataStoresGetSelects, GetInstance, Init, NewEntry, RecountEntries, Reset, Retrieve, SetFolder, Update
                    switch (methodUppered)
                    {
                        case "RESET":
                        case "UPDATE":
                        case "RETRIEVE":
                            this.FilterKeyword = TraceDocument.FILTER_INSTANCE_RUR;
                            break;
                        default:
                            this.FilterKeyword = TraceDocument.FILTER_INSTANCE_ALL;
                            break;
                    }
                    break;
                case "LOGIN":
                    this.FilterKeyword = "*";        // Zobrazí se vždy
                    break;
                case "MENUACTION":
                case "ONDBLCLICK":
                    this.FilterKeyword = TraceDocument.FILTER_FORMS;
                    break;
                case "ONROWCHANGED":
                    this.FilterKeyword = TraceDocument.FILTER_INSTANCE_CHANGE;
                    break;
                case "OPENWINDOW":
                    this.FilterKeyword = TraceDocument.FILTER_FORMS_OPEN;
                    break;
                case "PERIODS":
                case "PROGRESS":
                    this.FilterKeyword = TraceDocument.FILTER_FORMS;
                    break;
                case "RECORDRULES":       // FlowDefaults, Reset, TestSave
                    this.FilterKeyword = TraceDocument.FILTER_INSTANCE_IOM;
                    break;
                case "RELATIONS":         // SetFolder, 
                    this.FilterKeyword = TraceDocument.FILTER_INSTANCE_CHANGE;
                    break;
                case "ROOT":
                case "SHOWERROR":
                    this.FilterKeyword = TraceDocument.FILTER_FORMS;
                    break;
                case "SQL":               // _CreateSummaryTable, _DoUpdate, _ExecuteNonQuery, _GetDynRelCountForAllFolders, _getvalue, _LoadMore, ExecuteScalar, Fill(1), GetDbDataReader
                    this.FilterKeyword = TraceDocument.FILTER_SQL;
                    break;
                case "STORAGE":
                case "STORES":
                case "SWT":
                case "USERSCOUNT":
                    this.FilterKeyword = TraceDocument.FILTER_CACHE;
                    break;
                case "TEMPLATE":
                    this.FilterKeyword = TraceDocument.FILTER_FORMS;
                    break;
                case "THREAD":            // ProcessRequest
                    this.FilterKeyword = TraceDocument.FILTER_THREAD;
                    break;
                case "TRANS":             // _CheckState, Begin, End(Begin), End(End)
                    if (this.BSE == LineBSE.System && methodUppered == "_CHECKSTATE")
                        this.FilterKeyword = "";        // TRANS typu S je _CheckState, generuje položku Info, a generuje jich moc moc moc: ty zobrazím jen když uživatel chce VŠE!
                    else
                        this.FilterKeyword = TraceDocument.FILTER_SQL;
                    break;
                case "TUNNEL":
                    this.FilterKeyword = TraceDocument.FILTER_THREAD;
                    break;
                case "WARNING":
                    this.FilterKeyword = TraceDocument.FILTER_SYSMSG_WARNING;
                    break;
                default:
                    //MAF.APOLLO+
                    if (keywordUppered.StartsWith("COMMUNICATION", StringComparison.Ordinal)
                        || keywordUppered.StartsWith("DATASYNCHRONIZATION",StringComparison.Ordinal)
                        || keywordUppered.StartsWith("PAGES", StringComparison.Ordinal)
                        || keywordUppered.StartsWith("BIZ", StringComparison.Ordinal)
                        )
                        this.FilterKeyword = TraceDocument.FILTER_FORMS;
                    //MAF.APOLLO-
                    else
                        this.FilterKeyword = "";         // Jen když uživatel zaškrtne VŠE
                    break;
            }
        }
		/// <summary>
		/// Specialita: je tato událost chybou typu Deadlock?
		/// </summary>
		public bool IsDeadlock
		{
			get
			{
				if (this._Level != LineIWE.Error) return false;         // Když to není chyba, není to deadlock.

				string error = this.User(0).ToLower();
				string user1 = (this.UserLength >= 2 ? this.User(1).ToLower() : String.Empty);
				return (error.Contains("deadlock") ||
                        error.Contains("došlo ke kolizi s požadavkem") ||
                        error.Contains("deadlockuserfriendly") ||
                        user1.Contains("deadlock victim"));
			}
		}
        #endregion
    }
    #endregion
    #region Konstanty NorisApp
    /// <summary>
    /// Konstanty používané v datech aplikačního serveru
    /// </summary>
    public class ApolloAplConstants
    {
		/// <summary>
		/// NorisAplServerTrace (druh dat v trace souboru)
		/// </summary>
		internal const string TraceType = "ApolloAplServerTrace";
        /// <summary>
        /// 20_User
        /// </summary>
        internal const string NodeUser = "20_User";
        /// <summary>
        /// \20_User
        /// </summary>
        internal const string RootNodeUser = "\\" + NodeUser;
        /// <summary>
        /// 30_AplSrv
        /// </summary>
        internal const string NodeAplServer = "30_AplSrv";
        /// <summary>
        /// \30_AplSrv
        /// </summary>
        internal const string RootNodeAplServer = "\\" + NodeAplServer;
        /// <summary>
        /// ""  (relativní cesta z HomeNode do HomeNode je prázdný string)
        /// </summary>
        internal const string SubNodeUser = "";
        /// <summary>
        /// "10_SqlCommand" : relativní cesta pod uživatelem HomeNode
        /// </summary>
        internal const string SubNodeUserSql = "10_DbCommand";
        /// <summary>
        /// "20_Cowley" : relativní cesta pod uživatelem HomeNode
        /// </summary>
        internal const string SubNodeUserCowley = "20_BizFunction";
        /// <summary>
        /// "30_Instance" : relativní cesta pod uživatelem HomeNode
        /// </summary>
        internal const string SubNodeUserInstance = "30_BizRecord";
        /// <summary>
        /// "40_Kernel" : relativní cesta pod uživatelem HomeNode
        /// </summary>
        internal const string SubNodeUserKernel = "40_Kernel";
        /// <summary>
        /// "10_Info" : název subnodu Info
        /// </summary>
        internal const string SubNodeInfo = "10_Info";
        /// <summary>
        /// "20_Warning" : název subnodu Warning
        /// </summary>
        internal const string SubNodeWarning = "20_Warning";
        /// <summary>
        /// "30_Error" : název subnodu Error
        /// </summary>
        internal const string SubNodeError = "30_Error";
        /// <summary>
        /// "40_Kernel\10_Info" : relativní cesta pod uživatelem HomeNode k řádku Jádro/Info
        /// </summary>
        internal const string SubNodeUserKernelInfo = SubNodeUserKernel + "\\" + SubNodeInfo;
        /// <summary>
        /// "10_Memory" : název subnodu Aplikační server/Memory
        /// </summary>
        internal const string SubNodeAplMemory = "10_Memory";
        /// <summary>
        /// "20_SqlCommand" : název subnodu Aplikační server/SqlCommand
        /// </summary>
        internal const string SubNodeAplSql = "20_DbCommand";
        /// <summary>
        /// "30_System" : název subnodu Aplikační server/System
        /// </summary>
        internal const string SubNodeAplSystem = "30_System";
        /// <summary>
        /// "\30_AplSrv\10_Memory" : plná cesta k nodu Aplikační server/Memory
        /// </summary>
        internal const string RootNodeAplMemory = RootNodeAplServer + "\\" + SubNodeAplMemory;
        /// <summary>
        /// "\30_AplSrv\20_SqlCommand" : plná cesta k nodu Aplikační server/SqlCommand
        /// </summary>
        internal const string RootNodeAplSql = RootNodeAplServer + "\\" + SubNodeAplSql;
        /// <summary>
        /// "\30_AplSrv\30_System" : plná cesta k nodu Aplikační server/System
        /// </summary>
        internal const string RootNodeAplSystem = RootNodeAplServer + "\\" + SubNodeAplSystem;
        /// <summary>
        /// "\30_AplSrv\30_System\10_Info" : plná cesta k nodu Aplikační server/System/Info
        /// </summary>
        internal const string RootNodeAplSystemInfo = RootNodeAplServer + "\\" + SubNodeAplSystem + "\\" + SubNodeInfo;
        /// <summary>
        /// "\30_AplSrv\30_System\20_Warning" : plná cesta k nodu Aplikační server/System/Warning
        /// </summary>
        internal const string RootNodeAplSystemWarning = RootNodeAplServer + "\\" + SubNodeAplSystem + "\\" + SubNodeWarning;
        /// <summary>
        /// "\30_AplSrv\30_System\30_Error" : plná cesta k nodu Aplikační server/System/Error
        /// </summary>
        internal const string RootNodeAplSystemError = RootNodeAplServer + "\\" + SubNodeAplSystem + "\\" + SubNodeError;
        /// <summary>
        /// "Kernel" : prefix ColorGroup pro všechny elementy jádra, bez tečky
        /// </summary>
        internal const string ColorGroupKernel = "Kernel";
        /// <summary>
        /// "Kernel." : prefix ColorGroup pro všechny elementy jádra
        /// </summary>
        internal const string ColorGroupKernelDot = ColorGroupKernel + ".";
        /// <summary>
        /// "NrsCowley" : prefix ColorGroup pro všechny elementy funkcí, bez tečky
        /// </summary>
        internal const string ColorGroupCowley = "BizFunction";
        /// <summary>
		/// "NrsCowley." : prefix ColorGroup pro všechny elementy funkcí
		/// </summary>
		internal const string ColorGroupCowleyDot = ColorGroupCowley + ".";
        /// <summary>
        /// "Instance" : prefix ColorGroup pro všechny elementy instance, bez tečky
        /// </summary>
        internal const string ColorGroupInstance = "BizRecord";
        /// <summary>
        /// "Instance." : prefix ColorGroup pro všechny elementy instance
        /// </summary>
        internal const string ColorGroupInstanceDot = ColorGroupInstance + ".";
    }
    #endregion
}
